/* src/ircd_lexer.l
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 *  USA
 *
 * $Id: ircd_lexer.l 26606 2009-06-22 03:32:44Z androsyn $
 */

%option case-insensitive
%option noyywrap
%option nounput
%{

#include "ratbox_lib.h"
#include "stdinc.h"
#include "newconf.h"
#include "ircd_parser.h"
#include "common.h"
#include "s_log.h"

int conf_fgets(char *lbuf, int max_size, FILE * fb);

int yylex(void);

/* here to fixup gcc 3.3 errors */
int yyget_lineno(void);
FILE *yyget_in(void);
FILE *yyget_out(void);
int yyget_leng(void);
char *yyget_text(void);
void yyset_lineno(int  line_number);
void yyset_in(FILE *  in_str);
void yyset_out(FILE *  out_str);
int yyget_debug(void);
void yyset_debug(int  bdebug);
int yylex_destroy(void);


#define MAX_INCLUDE_DEPTH 10

YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
int include_stack_ptr=0;
int lineno = 1;
void ccomment(void);
void cinclude(void);
void hashcomment(void);
int ieof(void);
int lineno_stack[MAX_INCLUDE_DEPTH];
char conffile_stack[MAX_INCLUDE_DEPTH][IRCD_BUFSIZE];
extern char conffilebuf[IRCD_BUFSIZE+1];
char *current_file = conffilebuf;

FILE *inc_fbfile_in[MAX_INCLUDE_DEPTH];

char linebuf[512];

#undef YY_INPUT

#define YY_INPUT(buf,result,max_size) \
	(result = conf_fgets(buf, max_size, conf_fbfile_in))
%}

ws        [ \t]*
digit     [0-9]
comment   #.*
qstring   \"[^\"\n]*[\"\n]
string    [a-zA-Z_\~][a-zA-Z0-9_]*
include   \.include{ws}(\<.*\>|\".*\")

%%
{include}       { cinclude(); }
"/*"            { ccomment(); }
\n.*            { strcpy(linebuf, yytext+1); lineno++; yyless(1); }

{ws}            ;
{comment}       { hashcomment(); }

{digit}+        { yylval.number = atoi(yytext); return NUMBER; }

{qstring} {
		if(yytext[yyleng-2] == '\\')
		{
			yyless(yyleng-1); /* return last quote */
			yymore();         /* append next string */
		}
		else
		{
			strcpy(yylval.string, yytext + 1);
			if(yylval.string[yyleng-2] != '"') 
			{
				ilog(L_MAIN, "Unterminated character string");
			} 
			else
                        {
                        	int i,j;
                        	yylval.string[yyleng-2] = '\0'; /* remove close
                        					 *  quote 
								 */
                          
				for (j=i=0 ;yylval.string[i] != '\0'; i++,j++)
				{
					if (yylval.string[i] != '\\')
					{
						yylval.string[j] = yylval.string[i];
					}
					else
					{
						i++;
						if (yylval.string[i] == '\0')  /* XXX should not happen */
						{
							ilog(L_MAIN, "Unterminated character string");
							break;
						}
						yylval.string[j] = yylval.string[i];
					}
				}
				yylval.string[j] = '\0';
				return QSTRING;
			}
		}
	}


loadmodule	{ return LOADMODULE; }
{string}	{ 
			strcpy(yylval.string, yytext);
			yylval.string[yyleng] = '\0';
			return STRING;
		}

\.\.		{ return TWODOTS; }
.		{ return yytext[0]; }
<<EOF>>		{ if (ieof()) yyterminate(); }
%%

/* C-comment ignoring routine -kre*/
void ccomment()
{
	int c;
  
	/* log(L_NOTICE, "got comment"); */
	while (1)
	{
		while ((c = input()) != '*' && c != EOF)
		{
			if(c == '\n')
				++lineno;
                        
		}

		if (c == '*')
		{
			while ((c = input()) == '*');
			
			if (c == '/') 
				break;

			if (c == '\n')
				++lineno;
		}

		if (c == EOF)
		{
			conf_report_error("EOF in comment");
			break;
		}
	}
}

void cinclude(void)
{
	char *c;
	if ((c = strchr(yytext, '<')) == NULL)
		*strchr(c = strchr(yytext, '"') + 1, '"') = 0;
	else
		*strchr(++c, '>') = 0;

	/* do stacking and co. */ 
	if (include_stack_ptr >= MAX_INCLUDE_DEPTH) 
	{
		conf_report_error("Includes nested too deep (max is %d)", MAX_INCLUDE_DEPTH);
	}
	else
	{
		FILE *tmp_fbfile_in;
    
		tmp_fbfile_in = fopen(c, "r");
    
		if (tmp_fbfile_in == NULL)
		{
			/* if its not found in PREFIX, look in ETCPATH */
			char fnamebuf[IRCD_BUFSIZE];

			rb_snprintf(fnamebuf, sizeof(fnamebuf), "%s/%s", ETCPATH, c);
			tmp_fbfile_in = fopen(fnamebuf, "r");

			/* wasnt found there either.. error. */
			if(tmp_fbfile_in == NULL)
			{
				conf_report_error("Include %s: %m.", c);
				return;
			}
		}
		lineno_stack[include_stack_ptr] = lineno;
		lineno = 1;
		inc_fbfile_in[include_stack_ptr] = conf_fbfile_in;
		strcpy(conffile_stack[include_stack_ptr], c);
		current_file = conffile_stack[include_stack_ptr];
		include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER;
		conf_fbfile_in = tmp_fbfile_in;
		yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
	}
}

int ieof(void)
{
	if (include_stack_ptr)
		fclose(conf_fbfile_in);

	if (--include_stack_ptr < 0)
	{
		/* We will now exit the lexer - restore init values if we get /rehash
		 * later and reenter lexer -kre */
		 
		 include_stack_ptr = 0;
		 lineno = 1;
		 return 1;
	}

	/* switch buffer */
	yy_delete_buffer(YY_CURRENT_BUFFER);
	lineno = lineno_stack[include_stack_ptr];
	conf_fbfile_in = inc_fbfile_in[include_stack_ptr];

	if(include_stack_ptr)
		current_file = conffile_stack[include_stack_ptr];
	else
		current_file = conffilebuf;

	yy_switch_to_buffer(include_stack[include_stack_ptr]);
	return 0;
}

/* #-comment style, look for #include */
#define INCLUDE "#include"

void hashcomment(void)
{
	if (strlen(yytext) < sizeof(INCLUDE) - 1)
		return;

	if (!strncasecmp(yytext, INCLUDE, sizeof(INCLUDE) - 1))
		yyerror("You probably meant '.include', skipping");
}
